﻿using DotNetCore.CAP;
using DotNetCore.CAP.Internal;
using DotNetCore.CAP.Messages;
using DotNetCore.CAP.Monitoring;
using DotNetCore.CAP.Persistence;
using DotNetCore.CAP.Serialization;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace DotNetCore.CAP.NoStorage
{
    internal class NoStorageStorage : IDataStorage
    {
        private readonly IOptions<CapOptions> capOptions;
        private readonly ISerializer serializer;

        public NoStorageStorage(IOptions<CapOptions> capOptions, ISerializer serializer)
        {
            this.capOptions = capOptions;
            this.serializer = serializer;
        }

        public static Dictionary<string, NoStoreMessage> PublishedMessages { get; } = new Dictionary<string, NoStoreMessage>();

        public static Dictionary<string, NoStoreMessage> ReceivedMessages { get; } = new Dictionary<string, NoStoreMessage>();

        public Task ChangePublishStateAsync(MediumMessage message, StatusName state)
        {
            return Task.CompletedTask;
        }

        public Task ChangeReceiveStateAsync(MediumMessage message, StatusName state)
        {
            return Task.CompletedTask;
        }

        public MediumMessage StoreMessage(string name, Message content, object transaction = null)
        {
            var message = new MediumMessage
            {
                DbId = content.GetId(),
                Origin = content,
                Content = serializer.Serialize(content),
                Added = DateTime.Now,
                ExpiresAt = null,
                Retries = 0
            };

            return message;
        }

        public void StoreReceivedExceptionMessage(string name, string group, string content)
        { 
        }

        public MediumMessage StoreReceivedMessage(string name, string @group, Message message)
        {
            var mediumMessage = new MediumMessage
            {
                DbId = SnowflakeId.Default().NextId().ToString(),
                Origin = message,
                Added = DateTime.Now,
                ExpiresAt = null,
                Retries = 0
            }; 

            return mediumMessage;
        }

        public Task<int> DeleteExpiresAsync(string table, DateTime timeout, int batchCount = 1000, CancellationToken token = default)
        {
            var removed = 0;
            if (table == nameof(PublishedMessages))
            {
                var ids = PublishedMessages.Values
                    .Where(x => x.ExpiresAt < timeout)
                    .Select(x => x.DbId)
                    .Take(batchCount);

                foreach (var id in ids)
                {
                    if (PublishedMessages.Remove(id))
                    {
                        removed++;
                    }
                }
            }
            else
            {
                var ids = ReceivedMessages.Values
                    .Where(x => x.ExpiresAt < timeout)
                    .Select(x => x.DbId)
                    .Take(batchCount);

                foreach (var id in ids)
                {
                    if (ReceivedMessages.Remove(id))
                    {
                        removed++;
                    }
                }
            }

            return Task.FromResult(removed);
        }

        public Task<IEnumerable<MediumMessage>> GetPublishedMessagesOfNeedRetry()
        {
            var ret = PublishedMessages.Values
                .Where(x => x.Retries < capOptions.Value.FailedRetryCount
                            && x.Added < DateTime.Now.AddSeconds(-10))
                .Take(200)
                .Select(x => (MediumMessage)x);

            foreach (var message in ret)
            {
                message.Origin = serializer.Deserialize(message.Content);
            }

            return Task.FromResult(ret);
        }

        public Task<IEnumerable<MediumMessage>> GetReceivedMessagesOfNeedRetry()
        {
            var ret = ReceivedMessages.Values
                 .Where(x => x.Retries < capOptions.Value.FailedRetryCount
                             && x.Added < DateTime.Now.AddSeconds(-10))
                 .Take(200)
                 .Select(x => (MediumMessage)x);

            foreach (var message in ret)
            {
                message.Origin = serializer.Deserialize(message.Content);
            }

            return Task.FromResult(ret);
        }

        public IMonitoringApi GetMonitoringApi()
        {
            return new InMemoryMonitoringApi();
        }
    }
}
